package cn.genmer.test.security.rpc.v2;

import cn.genmer.test.security.rpc.SerialUtil;
import cn.genmer.test.security.rpc.v2.proxy.GProxy;
import cn.genmer.test.security.rpc.v2.rpc.Dispatcher;
import cn.genmer.test.security.rpc.v2.rpc.protocol.GContent;
import cn.genmer.test.security.rpc.v2.rpc.transport.GHttpRpcHandler;
import cn.genmer.test.security.rpc.v2.service.Bicycle2;
import cn.genmer.test.security.rpc.v2.service.Car2;
import cn.genmer.test.security.rpc.v2.service.Driver2;
import cn.genmer.test.security.rpc.v2.service.impl.MyBicycle2;
import cn.genmer.test.security.rpc.v2.service.impl.MyCar2;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.junit.Test;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.util.concurrent.atomic.AtomicInteger;

// &nbsp;

/**
 * 手写PRC框架
 *
 * 多线程 并发通过一个连接发送后，服务端解bytebuf转对象的过程
 */
//@SpringBootTest
//@RunWith(SpringRunner.class)
public class MyRPCTestV2 {
    /*
    1，先假设一个需求，写一个RPC
    2，来回通信，连接数量，拆包？
    3，动态代理呀，序列化，协议封装
    4，连接池
    5，就像调用本地方法一样去调用远程的方法，面向java中就是所谓的 面向interface开发
 */

    @Test
    public void startServer(){
        System.out.println("server started......");

        MyCar2 car = new MyCar2();
        MyBicycle2 bicycle = new MyBicycle2();

        Dispatcher dispatcher = Dispatcher.getDispatcher();
        dispatcher.register(Car2.class.getName(),car);
        dispatcher.register(Bicycle2.class.getName(),bicycle);

        NioEventLoopGroup boss = new NioEventLoopGroup(20);
        NioEventLoopGroup worker = boss;

        ServerBootstrap sbs = new ServerBootstrap();
        ChannelFuture bind = sbs.group(boss, worker)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                        System.out.println("【Server】server accept client :" + nioSocketChannel.remoteAddress().getAddress() + " " + nioSocketChannel.remoteAddress().getPort());
                        ChannelPipeline pipeline = nioSocketChannel.pipeline();
                        // 1.自定义RPC - 在自定义协议的时候遇到的问题： 粘包拆包 header+body
//                        pipeline.addLast(new GCommonDecode());
//                        pipeline.addLast(new GServerRequestHandler(dispatcher));
                        // 2. 借用HTTP 可以字节码就byte[] 也可以用netty提供的
                        pipeline.addLast(new HttpServerCodec())
                                .addLast(new HttpObjectAggregator(1024*512))
                                .addLast(new ChannelInboundHandlerAdapter(){
                                    @Override
                                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                        // http协议 这个msg是什么 - 完整的HTTP Request
                                        FullHttpRequest request = (FullHttpRequest)msg;
                                        System.out.println(request.touch()); // 因为consumer用的是一个现成的URL

                                        ByteBuf content = request.content();
                                        GContent gContent = SerialUtil.parseGContent(content);

                                        String serviceName = gContent.getName();
                                        String methodName = gContent.getMethodName();
                                        Object res = null;
                                        try{
                                            Object obj = dispatcher.get(serviceName);
                                            Class<?> clazz = obj.getClass();
                                            Method method = clazz.getMethod(methodName, gContent.getParameterTypes());
                                            res = method.invoke(obj,gContent.getArgs());
                                        }catch (Exception e){
                                            e.printStackTrace();
                                        }

                                        GContent responseContent = new GContent();
                                        // 设置返回信息
                                        responseContent.setRes(res);
                                        byte[] contentByte = SerialUtil.serialObject(responseContent);

                                        DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.OK, Unpooled.copiedBuffer(contentByte));
                                        // 不设置服务都读取会报content-length: 0 EOF
                                        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, contentByte.length);

                                        // HTTP 协议Header+Body
                                        ctx.writeAndFlush(response);
                                    }
                                });
                    }
                }).bind(new InetSocketAddress("127.0.0.1", 7777));
        try {
            bind.sync().channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    @Test
    public void startHttpServer(){
        MyCar2 car = new MyCar2();
        MyBicycle2 bicycle = new MyBicycle2();

        Dispatcher dispatcher = Dispatcher.getDispatcher();
        dispatcher.register(Car2.class.getName(),car);
        dispatcher.register(Bicycle2.class.getName(),bicycle);

        Server server = new Server(new InetSocketAddress("localhost", 7777));
        ServletContextHandler handler = new ServletContextHandler(server, "/");
        server.setHandler(handler);

        handler.addServlet(GHttpRpcHandler.class,"/*");

        try {
            server.start();
            server.join();
        } catch (Exception e) {


        }
    }

    // 模拟consumer端 && provider
    @Test
    public void get(){

//        new Thread(()->{
//            startServer();
//        }).start();


        AtomicInteger num = new AtomicInteger(0);
        int size = 20;
        Thread[] threads = new Thread[size];
        for (int i = 0; i < size; i++) {
            threads[i] = new Thread(()->{
                // 实现动态代理 - 存在本地调用可能
                Car2 car = GProxy.proxyGet(Car2.class);
                String arg = "小飞棍" + num.incrementAndGet();
                String res = car.start(arg);
                System.out.println("调用返回结果： " + res + " | 客户端参数： " + arg);
            });
        }

        for (Thread thread:threads){
            thread.start();
        }
        try {
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 实现动态代理
//        Car car = proxyGet(Car.class);
//        car.start("汽车开起来了！");

    }


    @Test
    public void testRPC(){

//        new Thread(()->{
//            startServer();
//        }).start();


        // 实现动态代理 - 存在本地调用可能
        AtomicInteger num = new AtomicInteger(0);
        Car2 car = GProxy.proxyGet(Car2.class);
        String name = "小飞棍" + num.incrementAndGet();
        Driver2 driver2 = car.registerDriver(name, num.get());
        System.out.println("调用返回结果： " + driver2 + " | 客户端参数： " + num);

    }

}